/*
 * @author kanghua(kanghua151@msn.com) 
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <glib.h>
#include <string.h>
#include "hlfs.h"
#include "comm_define.h"
//#include "address.h"
#include "misc.h"
#include "address.h"



int load_log(void *storage_handler, guint64 storage_address, struct log_header **log)
{
    int ret = 0;
    gchar *path = (gchar*)storage_handler;
    uint32_t offset = get_offset(storage_address); 
    gchar *segfile_name = build_segfile_name(get_segno(storage_address));
    gchar *segfile_path = g_build_filename(path, segfile_name, NULL);
    struct log_header lh;
    int fd = open(segfile_path, O_RDONLY);

    if (fd == -1) {
       ret = -1;
       goto out;
    } 
    if (LOG_HEADER_LENGTH != pread(fd, &lh, LOG_HEADER_LENGTH, offset)) {
        ret = -1;
        goto out;
    }
    *log = (struct log_header *)g_malloc0(lh.log_size);
    if (!log) {
	    g_message("%s -- allocate error!\n", __func__);
	    return -1;
    }
    if (lh.log_size != pread(fd, (char*)*log, lh.log_size, offset)){
        ret = -1;
        g_free(*log);
        goto out;
    }
out:
    g_free(segfile_name);
    g_free(segfile_path);
    return ret;
}

/*  return -1 mean spce  read fault
 *  return 1  mean space has not write yet */

int load_block_by_no(struct hlfs_ctrl *ctrl,uint64_t no,char **block){


    uint64_t storage_address ;
    guint32 BLOCKSIZE = ctrl->sb.block_size;
    uint32_t db_no = no;
    if(is_db_in_level1_index_range(db_no)){
        int _idx = db_no % 12;
        storage_address = ctrl->inode.blocks[_idx];
    }else if (is_db_in_level2_index_range(db_no)){
        if(ctrl->inode.iblock == 0){
          return 1;
        }
        uint64_t *_ib = (uint64_t *)read_block(ctrl->storage_handler,ctrl->inode.iblock,BLOCKSIZE);
        if(_ib==NULL) return -1;
        int  _idx = (db_no-12)%1024;
        storage_address = *(_ib+_idx);
        g_free(_ib);
    }else if (is_db_in_level3_index_range(db_no)){
        if(ctrl->inode.doubly_iblock ==0){
            return 1;
        }
        uint64_t *_ib = (uint64_t *)read_block(ctrl->storage_handler,ctrl->inode.doubly_iblock,BLOCKSIZE);
        if(_ib==NULL) return -1;
        int _idx   = ( db_no - 12 - 1024)/1024;
        if(*(_ib+_idx) == 0 ){
            return 1;
        }
        uint64_t *_ib2 = (uint64_t *)read_block(ctrl->storage_handler,*(_ib+_idx),BLOCKSIZE);
        if(_ib2==NULL) return -1;
        int _idx2  = (db_no - 12 - 1024)%1024;
        storage_address = *(_ib2 + _idx2);
        g_free(_ib);
        g_free(_ib2);
    }else if (is_db_in_level4_index_range(db_no)){
        if(ctrl->inode.triply_iblock == 0){
            return 1;
        }
        uint64_t *_ib  = (uint64_t *)read_block(ctrl->storage_handler,ctrl->inode.triply_iblock,BLOCKSIZE);
        if(_ib==NULL) return -1;
        int _idx   = (db_no -12 - 1024 - 1024*1024) / (1024*1024);
        if(*(_ib + _idx) == 0){
            return 1;
        }
        uint64_t *_ib2 = (uint64_t *)read_block(ctrl->storage_handler,*(_ib + _idx),BLOCKSIZE);
        if(_ib2==NULL) return -1;
        int _idx2  = ((db_no-12 - 1024 - 1024*1024)/1024)%1024;
        if(*(_ib2 + _idx2) == 0){
            return 1;
        }
        uint64_t *_ib3 = (uint64_t *)read_block(ctrl->storage_handler,*(_ib2 + _idx2),BLOCKSIZE);
        if(_ib3==NULL) return -1;
        int _idx3  = (db_no-12 - 1024 - 1024*1024) % 1024; 
        storage_address = *(_ib3 + _idx3);
        g_free(_ib);
        g_free(_ib2);
        g_free(_ib3);
    }
    g_message("storage_address:%llu\n",storage_address);
    if(storage_address == 0){
        return 1;
    }
    *block = read_block(ctrl->storage_handler,storage_address,BLOCKSIZE);
    if(*block ==NULL){
       g_message("can not read block for storage address %llu\n",storage_address);
       return -1;
    }
    return 0;

}

int load_block_by_addr(struct hlfs_ctrl *ctrl,uint64_t pos,char** block){
    guint32 BLOCKSIZE = ctrl->sb.block_size;
    uint32_t db_no = pos /BLOCKSIZE;
    return load_block_by_no(ctrl,db_no,block);
}

static int dump_log(struct hlfs_ctrl *ctrl,struct log_header *log){
    int ret = 0;
    gchar * path = (gchar*)ctrl->storage_handler;
    char *segfile_name = build_segfile_name(ctrl->last_segno);
    char *segfile_path = g_build_filename(path,segfile_name,NULL);
    int fd = g_open(segfile_path,O_CREAT|O_APPEND|O_WRONLY,00700);
    if(fd == -1){
       g_message("open file :%s failed\n",segfile_path);
       ret = -1;
       goto out;
    }
    int size = write(fd,(char*)log,log->log_size);
    if(size != log->log_size){
       g_message("write to file:%s failed, write size %d  expect size %d \n",segfile_path,size,log->log_size);
       ret = -1;
       goto out;
    } 
out:
    if(fd != -1) close(fd);
    g_free(segfile_name);
    g_free(segfile_path);
    return ret;
}


int append_log(struct hlfs_ctrl *ctrl,const char *db_buff,uint32_t db_start,uint32_t db_end){
    g_message(" -- entry func : %s -- \n",__func__);
    uint64_t* _ib  = NULL;
    uint64_t* _ib2 = NULL;
    uint64_t* _ib3 = NULL;
    guint32  ib_cur_offset = 0;
    guint32  db_offset = 0;
    guint32 BLOCKSIZE = ctrl->sb.block_size;
    guint32 db_data_len ;
    guint32 ib_data_len ;
    db_data_len = (db_end-db_start + 1) * BLOCKSIZE;
    ib_data_len = db_data_len + 3 * BLOCKSIZE;
    char* log_buff = (char*)g_malloc0(db_data_len + ib_data_len + 
            sizeof(struct inode) + sizeof(struct inode_map_entry) + 
            sizeof(struct log_header));
    if (!log_buff) {
	    g_message("%s -- allocate error!\n", __func__);
	    return -1;
    }
    guint32 ib_offset = db_data_len + LOG_HEADER_LENGTH;
    if(db_buff == NULL){
        db_data_len = 0;
        ib_offset = db_data_len + LOG_HEADER_LENGTH;
        goto __inode_create;  
    }
    guint32 db_cur_no = 0;
    guint32 i=0;
    g_message(" db_data_len:%d ib_data_len:%d BLOCKSIZE:%d\n",db_data_len,ib_data_len,BLOCKSIZE);
    for(db_cur_no = db_start,i=0; db_cur_no <= db_end; db_cur_no++,i++){
        char * cur_block_ptr = db_buff + i * BLOCKSIZE;  
        db_offset = LOG_HEADER_LENGTH + i*BLOCKSIZE;
        char * cur_log_buff_ptr = log_buff + db_offset;
        g_message(" db_cur_no:%d db_offset:%d \n",db_cur_no,db_offset);
        if(is_db_in_level1_index_range(db_cur_no)){
           g_message(" is level1 -- db_cur_no:%d db_offset:%d \n",db_cur_no,db_offset);
           /*  write db to log buff */
           int _idx = db_cur_no % 12;
           g_message(" idx:%u\n",_idx);
           set_segno (&ctrl->inode.blocks[_idx],ctrl->last_segno);
           set_offset(&ctrl->inode.blocks[_idx],ctrl->last_offset + db_offset);
           g_message("%s - log_buff:%p cur_log_buf:%p db_buff:%p cur_block_ptr:%p\n",__func__,log_buff,cur_log_buff_ptr,db_buff,cur_block_ptr);
           memcpy(cur_log_buff_ptr,cur_block_ptr,BLOCKSIZE);
        }else if(is_db_in_level2_index_range(db_cur_no)){
            g_message(" is level2 -- db_cur_no:%d db_offset:%d \n",db_cur_no,db_offset);
            //uint64_t * _ib = NULL;
            if(NULL == _ib){
                if(ctrl->inode.iblock == 0){
                    _ib = (uint64_t*)g_malloc0(BLOCKSIZE); 
                    if (!_ib) {
                        g_message("%s - g_malloc0 error!\n", __func__);
                        return -1;
                    }
                }else{
                    _ib = (uint64_t *)read_block(ctrl->storage_handler,ctrl->inode.iblock,BLOCKSIZE);
                    if (!_ib) {
                        g_message("%s - read block error!\n", __func__);
                        return -1;
                    }
                }
            }
            int  _idx = (db_cur_no - 12)%1024;
            g_message(" idx:%u\n",_idx);
            //uint64_t storage_address = *(_bi + _idx);
            set_segno ((_ib+_idx),ctrl->last_segno);
            set_offset((_ib+_idx),ctrl->last_offset + db_offset);
            memcpy(cur_log_buff_ptr,cur_block_ptr,BLOCKSIZE);
            if( (db_cur_no - 12 + 1) % 1024 == 0 || db_cur_no == db_end ){
                set_segno(&ctrl->inode.iblock,ctrl->last_segno);
                set_offset(&ctrl->inode.iblock,ctrl->last_offset + ib_offset);
                memcpy(log_buff + ib_offset,(char*)_ib,BLOCKSIZE);
                ib_offset +=BLOCKSIZE;
                g_free(_ib);
                _ib=NULL;
            }
            printf("ib >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> %p\n", _ib);
            //g_free(_ib);
        }else if(is_db_in_level3_index_range(db_cur_no)){
            g_message(" is level3 -- db_cur_no:%d db_offset:%d \n",db_cur_no,db_offset);
            //uint64_t *_ib = NULL;
            if(NULL == _ib){
                if(ctrl->inode.doubly_iblock == 0){
                    _ib = (uint64_t *)g_malloc0(BLOCKSIZE);
    		    if (!_ib) {
	    		g_message("%s -- allocate error!\n", __func__);
	    		return -1;
    			}
                }else{
                    _ib = (uint64_t *)read_block(ctrl->storage_handler,ctrl->inode.doubly_iblock,BLOCKSIZE);
    			if (!_ib) {
	    			g_message("%s -- allocate error!\n", __func__);
	    			return -1;
    			}
                }
            }
            int _idx   = (db_cur_no - 12 - 1024) / 1024;
            g_message(" idx:%u\n",_idx);
            //uint64_t *_ib2 = NULL; 
            if(NULL == _ib2){
                if(*(_ib+_idx) == 0 ){
                    _ib2 = (uint64_t*)g_malloc0(BLOCKSIZE);
    			if (!_ib2) {
	    			g_message("%s -- allocate error!\n", __func__);
	    			return -1;
    			}
                }else{
                    _ib2 = (uint64_t *)read_block(ctrl->storage_handler,*(_ib+_idx),BLOCKSIZE);
    			if (!_ib2) {
	    			g_message("%s -- allocate error!\n", __func__);
	    			return -1;
    			}
                }
            }
            int _idx2  = (db_cur_no - 12 - 1024)%1024;
            g_message(" idx2:%u\n",_idx2);
            set_segno ((_ib2+_idx2),ctrl->last_segno);
            set_offset((_ib2+_idx2),ctrl->last_offset + db_offset);
            memcpy(cur_log_buff_ptr,cur_block_ptr,BLOCKSIZE);

            if((db_cur_no -12 - 1024 + 1) % 1024 == 0 || db_cur_no == db_end){
              set_segno ((_ib+_idx),ctrl->last_segno);
              set_offset((_ib+_idx),ctrl->last_offset + ib_offset);
              memcpy(log_buff + ib_offset,(char*)_ib2,BLOCKSIZE);
              ib_offset +=BLOCKSIZE;
              g_free(_ib2);
              _ib2=NULL;
           }

           if((db_cur_no - 12 -1024 + 1) % (1024*1024) == 0 || db_cur_no == db_end){
              set_segno (&ctrl->inode.doubly_iblock,ctrl->last_segno);
              set_offset(&ctrl->inode.doubly_iblock,ctrl->last_offset + ib_offset);
              memcpy(log_buff + ib_offset,(char*)_ib,BLOCKSIZE);
              ib_offset +=BLOCKSIZE;
              g_free(_ib);
              _ib=NULL;
           }
           //g_free(_ib);
           //g_free(_ib2);
        }else if(is_db_in_level4_index_range(db_cur_no)){
            g_message(" is level4 -- db_cur_no:%d db_offset:%d \n",db_cur_no,db_offset);
            //uint64_t *_ib = NULL;
            if(NULL == _ib){
                if(ctrl->inode.triply_iblock == 0){
                    _ib = (uint64_t *)g_malloc0(BLOCKSIZE);
    			if (!_ib) {
	    			g_message("%s -- allocate error!\n", __func__);
	    			return -1;
    			}
                }else{
                    _ib = (uint64_t *)read_block(ctrl->storage_handler,ctrl->inode.triply_iblock,BLOCKSIZE);
    			if (!_ib) {
	    		g_message("%s -- allocate error!\n", __func__);
	    		return -1;
    			}
                }
            }
            int _idx   = (db_cur_no -12 -1024 - 1024*1024) / (1024 * 1024 * 1024);
            g_message(" idx:%u\n",_idx);
            //uint64_t *_ib2 = NULL; 
            if(NULL == _ib2){
                if(*(_ib+_idx) == 0 ){
                    _ib2 = (uint64_t*)g_malloc0(BLOCKSIZE);
    			if (!_ib2) {
	    			g_message("%s -- allocate error!\n", __func__);
	    			return -1;
    			}
                }else{
                    _ib2 = (uint64_t *)read_block(ctrl->storage_handler,*(_ib+_idx),BLOCKSIZE);
    			if (!_ib2) {
	    			g_message("%s -- allocate error!\n", __func__);
	    			return -1;
    			}
                }
            }
            int _idx2  = (db_cur_no - 12 - 1024 - 1024*1024) / (1024 * 1024);
            g_message(" idx2:%u\n",_idx2);
            //uint64_t *_ib3 = NULL; 
            if(NULL == _ib3){
                if(*(_ib2+_idx2) == 0 ){
                    _ib3 = (uint64_t*)g_malloc0(BLOCKSIZE);
    			if (!_ib3) {
	    			g_message("%s -- allocate error!\n", __func__);
	    			return -1;
    			}
                }else{
                    _ib3 = (uint64_t *)read_block(ctrl->storage_handler,*(_ib2+_idx2),BLOCKSIZE);
    			if (!_ib3) {
	    			g_message("%s -- allocate error!\n", __func__);
	    			return -1;
    			}
                }
            }
            int _idx3  = (db_cur_no -12 -1024 - 1024*1024) % 1024; 
            g_message(" idx3:%u\n",_idx3);
            set_segno ((_ib3+_idx3),ctrl->last_segno);
            set_offset((_ib3+_idx3),ctrl->last_offset + db_offset);
            memcpy(cur_log_buff_ptr,cur_block_ptr,BLOCKSIZE);

            if((db_cur_no-12-1024-1024*1024 + 1) % 1024 == 0 || db_cur_no == db_end){
                set_segno ((_ib2+_idx2),ctrl->last_segno);
                set_offset((_ib2+_idx2),ctrl->last_offset + ib_offset);
                memcpy(log_buff + ib_offset,(char*)_ib3,BLOCKSIZE);
                ib_offset +=BLOCKSIZE;
                g_free(_ib3);
                _ib3=NULL;
            }

            if((db_cur_no-12-1024-1024*1024 + 1) % (1024 * 1024)  == 0 || db_cur_no == db_end){
                set_segno ((_ib+_idx),ctrl->last_segno);
                set_offset((_ib+_idx),ctrl->last_offset + ib_offset);
                memcpy(log_buff + ib_offset,(char*)_ib2,BLOCKSIZE);
                ib_offset +=BLOCKSIZE;
                g_free(_ib2);
                _ib2=NULL;
            }

            if((db_cur_no-12-1024-1024*1024 + 1) % (1024*1024*1024) == 0 || db_cur_no == db_end){
                set_segno (&ctrl->inode.triply_iblock,ctrl->last_segno);
                set_offset(&ctrl->inode.triply_iblock,ctrl->last_offset + ib_offset);
                memcpy(log_buff + ib_offset,(char*)_ib,BLOCKSIZE);
                ib_offset +=BLOCKSIZE;
                g_free(_ib);
                _ib=NULL;
            }
            //g_free(_ib);
            //g_free(_ib2);
            //g_free(_ib3);
        }else{
           /* over limit size  */
           g_message("offset is out of limit size(8T) \n!!!");
           return -1;   
        }
   }
   __inode_create:;
        g_message("to update inode ...\n");
        int offset = ib_offset;
       // ctrl->inode.mtime  = 0;
       // ctrl->inode.ctime  = 0;
       // ctrl->inode.atime  = 0;
        g_message("to update inode map entry ...\n");
        g_message("last offset:%llu , last segno:%llu log head len:%d iboffset:%d\n", ctrl->last_offset,ctrl->last_segno,LOG_HEADER_LENGTH,ib_offset);
        ctrl->imap_entry.inode_no = HLFS_INODE_NO; 
        set_segno (&ctrl->imap_entry.inode_addr,ctrl->last_segno);     
        set_offset(&ctrl->imap_entry.inode_addr,ctrl->last_offset + offset);    
        g_message("inode address's offset %llu , give it %llu\n",ctrl->imap_entry.inode_addr,ctrl->last_offset + offset);
        memcpy(log_buff +  offset,&ctrl->inode,sizeof(struct inode));
        memcpy(log_buff +  offset + sizeof(struct inode),&ctrl->imap_entry,sizeof(struct inode_map_entry));
        g_message("to fill log header ...\n");
        struct log_header * lh = (struct log_header *)log_buff;
        lh->version = 0;
        lh->header_checksum = 0;
        lh->data_checksum = 0;
        lh->log_size = offset + sizeof(struct inode) + sizeof(struct inode_map_entry);
        lh->ctime = 0;
        lh->db_num = db_data_len/BLOCKSIZE;
        lh->ib_num = (ib_offset - db_data_len - LOG_HEADER_LENGTH)/BLOCKSIZE;
        g_message("log size:%d,log header:%d,inode:%d,inode map:%d,db:%d,ib:%d\n",lh->log_size,sizeof(struct log_header),sizeof(struct inode),sizeof(struct inode_map_entry),lh->db_num*BLOCKSIZE,lh->ib_num*BLOCKSIZE); 
        if(0!=dump_log(ctrl,lh)){
           g_message("log dump failed\n");
           g_assert(0);
        }
        int size = lh->log_size;
        g_message("return size :%d\n",lh->log_size);
        g_free(log_buff);
        return size;
}


int append_inode(struct hlfs_ctrl * ctrl){
    return append_log(ctrl,NULL,0,0);
}
